                                        
import japa.parser.JavaParser;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.FieldDeclaration;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.VariableDeclarator;
import japa.parser.ast.expr.AssignExpr;
import japa.parser.ast.expr.FieldAccessExpr;
import japa.parser.ast.expr.MethodCallExpr;
import japa.parser.ast.expr.NameExpr;
import japa.parser.ast.expr.ObjectCreationExpr;
import japa.parser.ast.expr.VariableDeclarationExpr;
import japa.parser.ast.stmt.BlockStmt;
import japa.parser.ast.type.ReferenceType;
import japa.parser.ast.visitor.VoidVisitorAdapter;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

public  class URIParser{	
	static BufferedWriter methodFile;	
	static BufferedWriter memberFile;
    static BufferedWriter accessesMethod;
	static BufferedWriter accesses;
	static BufferedWriter accessesAttribute; 
	static BufferedWriter accessesLocalAttributes; 
	static boolean insideConstructor=false;
	static boolean insideMethod=false;
	
	static BufferedWriter classNames;
	static BufferedWriter classIns;	
	static String [ ]objectCreationExpression=new String[1000];
	static String [ ]instanceExpr=new String[1000];
	static int noOfObject=0;	
	static int noOfInstanceExpr=0;
	static BlockStmt methodBody=null;
	
	@SuppressWarnings("unchecked")	
	public void parseSingleClass(String fileName) throws Exception {		    
	    FileInputStream in = new FileInputStream(fileName);
	    memberFile = new BufferedWriter(new FileWriter("InfoFiles/MembersClass.txt"));
        methodFile = new BufferedWriter(new FileWriter("InfoFiles/MethodsClass.txt"));        
        
        accessesLocalAttributes = new BufferedWriter(new FileWriter("InfoFiles/AccessesLocalMember.txt"));
        accessesAttribute = new BufferedWriter(new FileWriter("InfoFiles/AccessesMethods.txt"));
        accessesMethod = new BufferedWriter(new FileWriter("InfoFiles/AccessesAttributes.txt"));
        
        accesses = new BufferedWriter(new FileWriter("InfoFiles/AccessesClass.txt"));        
        
        CompilationUnit cu = null; 
        try {
        	cu = JavaParser.parse(in);
        	
        }
        catch(Exception e){
        	e.printStackTrace();
        }
        finally { 
            in.close();            
        }
       
        new ClassVisitor().visit(cu, null);
        //Get Local Class Instantiations Expression for instance, " className object = new className();"
        new LocalClassInstantiationVisitor().visit(cu, null);
        
    	//Get Class Instantiations Expression for instance, " className object = new className();"
    	new ClassInstantiationExpr().visit(cu,null);
    	
    	//Get Object Creation Expression for instance, "object = new className();"  
    	new ObjectCreationVisitor().visit(cu,null);
    	
    	//Get Object Name Expressions for instance, "new className();" 	        	
    	new ObjectNameVisitor().visit(cu,null);		        	
    	
    	//Get fields Accessed by an object			        	
    	new FieldAccessVisitor().visit(cu, null);
       
    	new MethodInvocationVisitor().visit(cu,null);
    	
        new MethodVisitor().visit(cu,null);
    
    	
    	new ClassReferencedVisitor().visit(cu,null);
    	
    	
    	new FieldAccessExprVisitor().visit(cu,null);
    	
    	methodFile.close();
        memberFile.close();       
        
        accessesMethod.close();
        accessesAttribute.close();
        accessesLocalAttributes.close();
        

        accesses.close();
	} 
	
	
	private static class ClassVisitor extends VoidVisitorAdapter<Object> {
        @Override
		public void visit(ClassOrInterfaceDeclaration  n, Object arg) {          
        } 	
	}	
	 /*
     * "visit(...)" method returns class members that are 
     * instantiating an object, For Instance, 
     * className object=new className(); changeFormat Bring 
     * this into format className object;  
     */
    private static class LocalClassInstantiationVisitor extends VoidVisitorAdapter<Object> {

    	public String changeFormatToInstanceExpr(String expr)
		{	
    		String instantiatedClass=null;
			String instanceName=null;
			String[] names=expr.split(" ");
			int index=names.length-5;
			instantiatedClass=names[index];
			instanceName=names[index+1];
			
			return instantiatedClass+" "+instanceName; //returns the result in the form "className object"
		
		}		        
        @Override
		public void visit(FieldDeclaration n, Object arg) {     	
        	try {		
        		
        		String declaredFields=n.toString();        		
        		if(declaredFields.contains(" new")){
        			String classInstanceExprFormat=changeFormatToInstanceExpr(n.toString());
        			instanceExpr[noOfInstanceExpr++]=new String(classInstanceExprFormat);        			
        		}		        	
			} catch (Exception e) {					
				e.printStackTrace();
			}		          
        }		        
    }
	/*
	 * "visit()" method in this class stores all 
	 * object creation expression "new className()" in the array objectCreationExpression
	 */	
	private static class ObjectCreationVisitor extends VoidVisitorAdapter<Object> {       
		@Override
		public void visit(ObjectCreationExpr n, Object arg) {     	
        	try {        		        			
        			//System.out.println("ObjectCreationExp "+n.toString());
        			objectCreationExpression[noOfObject++]=new String(n.toString());       			
        		
			}catch (Exception e) {					
						e.printStackTrace();
			}
          
        } 
    
	}
	 /*
	 *  "visit()" method in this class visits all   
	 *  instantiating expression "className object=new className()" within methods
	 *  and stores "className object" in instanceExpr array	     
	 */
	private static class ClassInstantiationExpr extends VoidVisitorAdapter<Object> {

		public String changeFormatToInstanceExpr(String expr)
		{			
							
			int index=0;
    		String instantiatedClass=null;
			String instanceName=null;	
			String[] names=expr.split(" ");
			for(int i=0; i<names.length; i++)
			{
				//System.out.println("Expression 1 "+i+" "+names[i]);	
			}			
			if(names.length>4)
			{
				index=names.length-5;
			}
			instantiatedClass=names[index];
			instanceName=names[index+1];
			//System.out.println("Class Expr "+instantiatedClass+" "+instanceName);
			return instantiatedClass+" "+instanceName; //returns the result in the form "className object
		
		}
	
		@Override
		public void visit(VariableDeclarationExpr  n, Object arg) {
     	
        	try {
        		
        			super.visit(n, arg);        			
        			//System.out.println("Class Instantiation Expression:  "+n.toString());
        			instanceExpr[noOfInstanceExpr++]=new String(changeFormatToInstanceExpr(n.toString()));
        			
        			
			} 	catch (Exception e) {					
						e.printStackTrace();
			}
          
        } 
	}
    
	 /*
     * "visit()" method evaluates all assignment expressions in the class and
     * uses objectCreationExpression array to find out an assignment that 
     * is used to create an instance of the object for instance object = new className();
     * "object=new className()" is the assignment expression as well as it is instantiating 
     * an instance for a class"
     */   
	private static class ObjectNameVisitor extends VoidVisitorAdapter<Object> {
		public String changeFormatToInstanceExpr(String expr)
		{		
			int endOfClassNameIndex;
			//System.out.println("O Expression "+expr);
			if(expr.indexOf("(")>0){
				endOfClassNameIndex=expr.indexOf("(");
			}
			else{
				endOfClassNameIndex=expr.length()-2;
			}
			String instantiatedClass=expr.substring(expr.indexOf("new ")+4, endOfClassNameIndex);
			String instanceName=expr.substring(0, expr.indexOf("=")-1);			
			return instantiatedClass+" "+instanceName;
		
		}
        @Override
		public void visit(AssignExpr n, Object arg) {
     	
        	try {
        			String expression=n.toString();        		
        			for(int i=0; i<noOfObject; i++){
        			
        				if(expression.contains(objectCreationExpression[i]))        						
        				{        					
        					instanceExpr[noOfInstanceExpr]=new String(changeFormatToInstanceExpr(expression));        					
        					noOfInstanceExpr++;
        				}
        			}
        		
			} 	catch (Exception e) {					
						e.printStackTrace();
				}
          
        } 
    
	}

	/*
	 * "visit()" method in this class is getting fields accessed by an object "object.fieldAccessed"	  
	 */
	private static class FieldAccessVisitor extends VoidVisitorAdapter<Object> {
		
		 @Override
		  public void visit(MethodDeclaration n, Object arg) {           
	        	
	        	try {
	        		
	        		insideMethod=true;
	        		accessesAttribute.write("Start of Method");	 
	        		accessesAttribute.newLine();
					accessesAttribute.write(n.getName());					
					//methodFile.write(n.getName());
					//methodFile.newLine();					
					accessesAttribute.newLine();
				
					if(n.getBody()!=null)
					visit(n.getBody(), arg);
					methodBody =n.getBody();
					accessesAttribute.write("End of Method");
		            accessesAttribute.newLine();
		            insideMethod=false;
	        		}
	        	catch (IOException e) {		
	        		
					e.printStackTrace();
				}
	        } 
	    
     	 @Override
		public void visit(FieldAccessExpr  n, Object arg) {
		        	
		        	try {
		        			super.visit(n, arg);
		        			String fieldAccessed=n.toString();		        			    			
		        			String fieldClassName=fieldAccessed.substring(0,fieldAccessed.indexOf("."));		        			
		        			for(int i=0; i<noOfInstanceExpr; i++){
		        				
		        				String className=instanceExpr[i].substring(0, instanceExpr[i].indexOf(" "));		        				
		        				String instanceName=instanceExpr[i].substring(instanceExpr[i].indexOf(" ")+1, instanceExpr[i].length());
			        			/*Compare fieldAccessed expression "object.fieldAccessed" with "className object" expression
			        			* to make it "className.fieldAccessed"
			        			*/ 
		        				if(insideMethod==true && fieldClassName.contentEquals(instanceName))
		        				{
		        					
		        					//System.out.println("the attribute's class is :"+className);
		        					accessesAttribute.write(className);						
	        						
		        					accessesAttribute.newLine();
		        					
		        				}	
		        			}	
		        			
						} catch (IOException e) {					
							e.printStackTrace();
						}
		          
		        	}

	} 
	
	private static class MethodInvocationVisitor extends VoidVisitorAdapter<Object> {
       
         public void visit(MethodDeclaration n, Object arg) {           
	        	
	        	try {	
	        		insideMethod=true;
	        		accessesMethod.write("Start of Method");	 
	        		accessesMethod.newLine();
					accessesMethod.write(n.getName());					
					methodFile.write(n.getName());
					methodFile.newLine();					
					accessesMethod.newLine();
					if(n.getBody()!=null)
					visit(n.getBody(), arg);
		            accessesMethod.write("End of Method");
		            accessesMethod.newLine();
		            insideMethod=false;
	        		}
	        	catch (IOException e) {		
	        		
					e.printStackTrace();
				}
	        } 
	    
        	
        	@Override
			public void visit(MethodCallExpr  n, Object arg) {
        	
        	try {
        		super.visit(n, arg);
    			String fieldAccessed=n.toString();
    			String field=n.getName()+"()";
    			String fieldClassName=null;
    			if(fieldAccessed.indexOf(".")>0){		        				
    				fieldClassName=fieldAccessed.substring(0,fieldAccessed.indexOf("."));
    			}
    			/*Compare methodAccessed expression "object.methodAccessed" with "className object" expression
    			* to make it "className.methodAccessed"
    			*/ 		        			
    			for(int i=0; i<noOfInstanceExpr; i++)
    			{
    				String className=instanceExpr[i].substring(0, instanceExpr[i].indexOf(" "));		        				
    				String instanceName=instanceExpr[i].substring(instanceExpr[i].indexOf(" ")+1, instanceExpr[i].length());
    				if(fieldClassName!=null && instanceName!=null)
    				{
    					if(insideMethod==true && fieldClassName.contentEquals(instanceName))
    					{
    						
    						//System.out.println("The class accessed is:"+className);
    						accessesMethod.write(className);
    						accessesMethod.newLine();
    					}
    				}
    			}
    		
			} 	catch (IOException e) {					
						e.printStackTrace();
				}
          
        } 
}
	private static class MethodVisitor extends VoidVisitorAdapter<Object> { 
        @Override
		public void visit(MethodDeclaration n, Object arg) {           
        	
        	try {	
        		insideMethod=true;
        		accessesLocalAttributes.write("Start of Method");	 
        		accessesLocalAttributes.newLine();
				accessesLocalAttributes.write(n.getName());					
				//methodFile.write(n.getName());
				//methodFile.newLine();					
				accessesLocalAttributes.newLine();
				if(n.getBody()!=null)
				visit(n.getBody(), arg);
	            accessesLocalAttributes.write("End of Method");
	            accessesLocalAttributes.newLine();
	            insideMethod=false;
        		}
        	catch (IOException e) {		
        		
				e.printStackTrace();
			}
        } 
        @Override
		public void visit(NameExpr ne, Object o){
        	try {
        		if(insideMethod==true){
					accessesLocalAttributes.write(ne.getName());						
					accessesLocalAttributes.newLine();
	        	}
			} catch (IOException e) {					
				e.printStackTrace();
			}
        } 
    } 


	private static class ClassReferencedVisitor extends VoidVisitorAdapter<Object> {
		
		 @Override
		  public void visit(ReferenceType  n, Object arg) {           
	        	
	        	try {		        		
	        		  super.visit(n,arg);
	        		  if(!n.toString().contentEquals("String") && !n.toString().contentEquals("String[]") )
	        		  { 	  
	        			  memberFile.write(n.toString());
	        			  memberFile.newLine();
	        		  }
	        	    }
	        	catch (IOException e) {		
	        		
					e.printStackTrace();
				}
	        } 
	    
    	 
	} 

	
	
	@SuppressWarnings("unchecked")
	private static class FieldAccessExprVisitor extends VoidVisitorAdapter { 
		
		boolean AnObjectName(String obj)
		{
			for(int i=0; i<noOfInstanceExpr; i++)
			{
				//System.out.println("Instance Expre "+instanceExpr[i]);
				String objectName=instanceExpr[i].substring(instanceExpr[i].indexOf(" ")+1, instanceExpr[i].length());
				//System.out.println(" Object Name "+objectName+" Length "+objectName.length());
				if(objectName.equals(obj)) 
				{
					return true;
				}
			}
			return false;
		}
		@Override
		public void visit(FieldDeclaration n, Object arg) { 
	    
		    List<VariableDeclarator> aa = n.getVariables();
	        try {		        	 
	        	    String fieldName=aa.get(0).toString();
	        	    
	        	    if(!AnObjectName(fieldName) && !fieldName.contentEquals("String") &&!fieldName.contentEquals("String[]"))
	        	    {
		        	    if(fieldName.indexOf(" ")>=0)
		        	    {   
		        	    	memberFile.write(fieldName.substring(0, fieldName.indexOf(" ")));
		        	    }
		        	    else if(fieldName.indexOf("=")>=0)
		        	    {
		        	    	memberFile.write(fieldName.substring(0, fieldName.indexOf("=")));
		        	    }
		        	    else{
		        	    	memberFile.write(fieldName);
		        	    }
		        	    memberFile.newLine();
	        	    }
			}
	        catch (IOException e) {				
				e.printStackTrace();
			}
	    } 
	}	
}
